﻿using BetterPlaceClient.BetterClientService;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Windows.Forms;

namespace BetterPlaceClient
{
    public partial class ClientGui : Form
    {
        private static IBetterClientService clientService = new BetterClientServiceClient();
        private List<Customer> _listCustomers = new List<Customer>(); // This will be removed once
                                                                      // login is functional.
        private bool _programStart; // First time loading

        private Reservation _resv = new Reservation();

        // End client global data
        //private List<Reservation> _resvTable = new List<Reservation>();
        public ClientGui()
        {
            try
            {
                InitializeComponent();
                StartProgressBar("Vent venligst, henter program data.");
                // First time the program loads we start two backgroundworkers, this makes them wait
                // for each other to finish before reseting the status.
                _programStart = true;
                bwRouteGetCustomers.RunWorkerAsync();
                bwRouteGetStations.RunWorkerAsync();
            }
            catch (TimeoutException tie)
            {
                ShowUserFriendlyError("Timeout exception: " + tie.Message, "Timeout exception");
            }
            catch (Exception e)
            {
                ShowUserFriendlyError("Exception: " + e.Message, "Exception");
            }
        }

        /// <summary>
        /// Cancel/Deny the displayed route, calls the clientService to release batteries.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClearRoute_Click(object sender, EventArgs e)
        {
            StartProgressBar("Vent venligst imens din rute annulleres.");
            clientService.CancelReservation(_resv.Id);
            _resv = new Reservation();
            routeBindingSource.Clear();
            if (!bwGetCustomerReservations.IsBusy)
            {
                bwGetCustomerReservations.RunWorkerAsync();
            }
            cmbRouteFromStation.Enabled = true;
            cmbRouteToStation.Enabled = false;
        }

        /// <summary>
        /// Accept the currently displayed route.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCommitRoute_Click(object sender, EventArgs e)
        {
            btnCommitRoute.Enabled = false;
            StartProgressBar("Vent venligst imens din reservation gemmes.");
            _resv.Status = ReservationsStatus.Accepted;
            bwRouteAcceptReservation.RunWorkerAsync();
            bwGetCustomerReservations.RunWorkerAsync();
            tabControl1.SelectTab(2);
            cmbRouteFromStation.Enabled = true;
            cmbRouteToStation.Enabled = false;
        }

        /// <summary>
        /// Makes a temporary reservation object, that is replaced by the real object once the
        /// GetRoute is done.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnFindRoute_Click(object sender, EventArgs e)
        {
            if (cmbRouteFromStation.SelectedIndex > -1 && cmbRouteToStation.SelectedIndex > -1)
            {
                StartProgressBar("Vent venligst imens din rute findes.");
                btnCommitRoute.Enabled = false;
                _resv = new Reservation
                {
                    Route = new Route(),
                    Customer = customerBindingSource.DataSource as Customer
                };
                _resv.Route.StartStation = (Station)cmbRouteFromStation.SelectedItem;
                _resv.Route.EndStation = (Station)cmbRouteToStation.SelectedItem;
                _resv.Date = dateTimeRoute.Value;

                if (!bwRouteGetRoute.IsBusy)
                    bwRouteGetRoute.RunWorkerAsync();
            }
        }

        /// <summary>
        /// Generate the toStation list, do not add the currently selected station.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbRouteFromStation_SelectedIndexChanged(object sender, EventArgs e)
        {
            var tmpStationList = stationFromBindingSource.DataSource as List<Station>;
            if (tmpStationList != null) stationToBindingSource.DataSource = new List<Station>(tmpStationList);
            stationToBindingSource.Remove(cmbRouteFromStation.SelectedItem);
            cmbRouteToStation.Enabled = true;
            btnFindRoute.Enabled = true;
        }

        /// <summary>
        /// Search the local customer list by name, no error handling implemented, since this data
        /// is the same as used to populate the dropdownlist that calls this method.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private Customer GetCustomerByName(string name)
        {
            return (from c in _listCustomers
                    where c.Name == name
                    select c).FirstOrDefault();
        }

        // GetListOfCustomers
        private void bwRouteGetCustomers_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = clientService.GetAllCustomers();
        }

        private void bwRouteGetCustomers_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            var personList = (PersonList)e.Result;
            if (personList != null && personList.SerStatus.Equals("Ok"))
            {
                _listCustomers = new List<Customer>(personList.SerData);
                if (_listCustomers.Any())
                {
                    cmbUsername.Items.Clear();
                    foreach (var c in _listCustomers)
                    {
                        cmbUsername.Items.Add(c.Name);
                    }
                    cmbUsername.Enabled = true;
                }
            }
            ResetProgressBar();
        }

        /// <summary>
        /// Call the clientService and get the route and reservation generated using Dijkstras
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bwRouteGetRoute_DoWork(object sender, DoWorkEventArgs e)
        {
            var customer = customerBindingSource.DataSource as Customer;
            if (customer != null)
                e.Result = clientService.CreateReservationUsingDijkstras(_resv.Route.StartStation.Id, _resv.Route.EndStation.Id, customer.Id, dateTimeRoute.Value);
        }

        private void bwRouteGetRoute_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            ReservationData resvData = e.Result as ReservationData;
            if (resvData != null && resvData.SerStatus.Equals("Ok"))
            {
                _resv = resvData.SerData;
                if (_resv != null) routeBindingSource.DataSource = _resv.Route;
            }
            else
            {
                if (resvData != null) ShowUserFriendlyError(resvData.SerErrorDescription, resvData.SerStatus);
            }
            ResetProgressBar();
            btnCommitRoute.Enabled = true;
        }

        // GetListOfStations
        private void bwRouteGetStations_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = clientService.GetAllStations();
        }

        private void bwRouteGetStations_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            var stationList = (StationList)e.Result;
            if (stationList != null && stationList.SerStatus.Equals("Ok"))
            {
                stationFromBindingSource.DataSource = new List<Station>(stationList.SerData);
                cmbRouteFromStation.Enabled = true;
            }
            ResetProgressBar();
        }

        private void bwGetCustomerReservations_DoWork(object sender, DoWorkEventArgs e)
        {
            var customer = customerBindingSource.DataSource as Customer;
            //Call the serverside function GetAllStation() and wait for it to finish
            if (customer != null) e.Result = clientService.GetAllReservationsFromCustomer(customer.Id);
        }

        private void bwGetCustomerReservations_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //Receive the result from DoWork and set reservationBinginSource.Datasource
            var reservationList = e.Result as ReservationList;
            if (reservationList != null && reservationList.SerStatus.Equals("Ok"))
            {
                var tmpList = (new List<Reservation>(reservationList.SerData));
                reservationsBindingSource.DataSource = tmpList;
                foreach (var reservation in tmpList)
                {
                    if (reservation.Status == ReservationsStatus.Created)
                    {
                        _resv = reservation;
                        if (reservation.Route != null) routeBindingSource.DataSource = reservation.Route;
                    }
                }
            }
            else
            {
                MessageBox.Show("Error in getting the reservations");
            }
            ResetProgressBar();
        }

        private void btnCancelReservation_Click(object sender, EventArgs e)
        {
            if (dataGridViewReservations.SelectedRows.Count > -1)
            {
                if (dataGridViewReservations.CurrentRow != null)
                {
                    Reservation r = dataGridViewReservations.CurrentRow.DataBoundItem as Reservation;
                    if (r != null) clientService.CancelReservation(r.Id);
                }
                StartProgressBar("Den valgte reservation slettes.");
                bwGetCustomerReservations.RunWorkerAsync();
                dataGridViewReservations.Update();
                dataGridViewReservations.Refresh();
                partReservationsBindingSource.Clear();
            }
        }

        private void btnLogin_Click(object sender, EventArgs e)
        {
            if (cmbUsername.SelectedIndex > -1)
            {
                customerBindingSource.DataSource = GetCustomerByName(cmbUsername.SelectedItem.ToString());
                bwGetCustomerReservations.RunWorkerAsync();
                tabControl1.SelectedIndex = 1;
            }
        }

        private void bwRouteAcceptReservation_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = clientService.UpdateReservation(_resv);
        }

        private void bwRouteAcceptReservation_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            var intData = (IntData)e.Result;
            if (intData.SerStatus.Equals("Ok"))
            {
                _resv = new Reservation();
                _resv.Route = new Route();
                routeBindingSource.DataSource = _resv.Route;
            }
            else
            {
                ShowUserFriendlyError(intData.SerErrorDescription, intData.SerStatus);
            }
            ResetProgressBar();
            btnCommitRoute.Enabled = true;
        }

        private void dataGridViewReservations_SelectionChanged(object sender, EventArgs e)
        {
            if (dataGridViewReservations.SelectedRows.Count > 0)
            {
                Reservation reservation = dataGridViewReservations.SelectedRows[0].DataBoundItem as Reservation;

                if (reservation != null)
                    partReservationsBindingSource.DataSource = reservation.PartReservations ?? new PartReservation[0];
            }
        }

        /// <summary>
        /// We are no longer loading anything, stop the load bar and tell the user.
        /// </summary>
        private void ResetProgressBar()
        {
            if (_programStart)
            {
                if (bwRouteGetCustomers.IsBusy && bwRouteGetStations.IsBusy)
                {
                    _programStart = false;
                }
            }
            toolStripProgressBar.Visible = false;
            toolStripStatusLabel.Text = @"Klar til brug";
        }

        /// <summary>
        /// Show a unified error, this is used to keep the code reuse to a minimum.
        /// </summary>
        /// <param name="serErrorDescription">The error description</param>
        /// <param name="serStatus">The status was not "Ok", show it as the title.</param>
        private void ShowUserFriendlyError(string serErrorDescription, string serStatus)
        {
            MessageBox.Show(serErrorDescription, serStatus, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }

        /// <summary>
        /// Show the statusbar, and print a message to show what is loading.
        /// </summary>
        /// <param name="progressDescription"></param>
        private void StartProgressBar(string progressDescription)
        {
            toolStripProgressBar.Visible = true;
            toolStripStatusLabel.Text = progressDescription;
        }
    }
}

// Some parts of the logic has to be defined, for use in this GUI.
namespace BetterPlaceClient.BetterClientService
{
    public partial class Customer
    {
        public override string ToString()
        {
            return Name;
        }
    }

    public partial class Route
    {
        public override string ToString()
        {
            return StartStation + " til " + EndStation;
        }
    }

    public partial class Station
    {
        public override string ToString()
        {
            return Name;
        }
    }
}